/*
    file.c
    Achieve the set of file function
    BeginnerC
*/
#include "file.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
/*
    ToAbsolutePath
        transform relative path to absolute path
    Argument
        relative_path
            The path want to transform
    Return Value
        The buffer to the path
*/
char* ToAbsolutePath(char *relative_path)
{
    int fd = 0;
    char *result = NULL;
    char command[1024] = {};
    fd = open(relative_path, O_RDONLY);
    if (fd < 0)
        return NULL;
    result = calloc(NAME_MAX + 1, sizeof(char));
    sprintf(command, "/proc/self/fd/%d", fd);
    readlink(command, result, NAME_MAX);
    close(fd);
    return result;
}
/*
    GetProcPath
        Get the program current absolute path
    Argument
        No argument require
    Return Value
        The buffer to the path
*/
char* GetProcPath()
{
    char buffer[NAME_MAX] = {};
    char *result = NULL;
    char *position = NULL;
    unsigned length = 0;
    readlink("/proc/self/exe", buffer, sizeof(buffer));
    position = strrchr(buffer, '/');
    length = position - buffer;
    result = calloc(length + 1, sizeof(char));
    if (NULL == result)
    {
        return NULL;
    }
    memcpy(result, buffer, length);
    return result;
}
/*
    IsAbsolutePath
        Judge a path is or not a absolute path
    Argument
        file_name
            The file name want to judge
    Return Value
        true
            Is
        false
            Not
*/
bool IsAbsolutePath(const char *file_name)
{
    if (NULL == file_name)
        return false;
    if (NULL == strchr(file_name, '/'))
        return false;
    if (*file_name == '.')
        return false;
    return true;
}
/* 
    CreateDirectory
        Create a directory 
    Argument
        directory_name
            The target directory name
    Return Value
        0
            Success
        -1
            Failed
*/
int CreateDirectory(const char *directory_name)
{
    return mkdir(directory_name, S_IRWXU | S_IRWXG | S_IRWXO);
}
/* 
    FileExists
        Judge a file is or not exist 
    Argument
        file_name
            The file name
    Return
        true
            Exist
        false
            Not exist
*/
bool FileExists(const char *file_name)
{
    return 0 == access(file_name, F_OK);
}
/* 
    GetFileName
        Get the file name by full path
    Argument
        file_name
            The full file name#include <sys/types.h>
        The position of the file name
        If not exist, return NULL
*/
char* GetFileName(char *file_name)
{
    char *position = NULL;
    if (NULL == file_name)
        return NULL;
    if ('\0' == *file_name)
        return NULL;
    position = strrchr(file_name, '/');
    if (position)
        return position + 1;
    return file_name;
}
/* 
    MoveDirectory
        Move a directory
    Argument
        src_path
            The source path
        dest_path
            The target path
    Return Value
        0
            Success
        -1
            Failed
*/
int MoveDirectory(const char *src_path, const char *dest_path)
{
    DIR *dir = NULL;
    struct dirent *file = NULL;
    dir = opendir(src_path);
    if (NULL == dir)
    {
        return -1;
    }
    CreateDirectory(dest_path);
    while (file = readdir(dir))
    {
        char src_file_path[NAME_MAX] = {};
        char dest_file_path[NAME_MAX] = {};
        if (!strcmp(".", file -> d_name) || !strcmp("..", file -> d_name))
        {
            continue;
        }
        sprintf(src_file_path, "%s/%s", src_path, file -> d_name);
        sprintf(dest_file_path, "%s/%s", dest_path, file -> d_name);
        MoveFile(src_file_path, dest_file_path);
        if (DT_DIR == file -> d_type)
        {
            MoveDirectory(src_file_path, dest_file_path);
        }
    }
    closedir(dir);
    rmdir(src_path);
    return 0;
}
/* Delete a directory */
int DeleteDirectory(const char *path)
{
    DIR *dir = NULL;
    struct dirent *file = NULL;
    dir = opendir(path);
    if (NULL == dir)
    {
        return -1;
    }
    while (file = readdir(dir))
    {
        char target_file_path[NAME_MAX] = {};
        if (!strcmp(".", file -> d_name) || !strcmp("..", file -> d_name))
        {
            continue;
        }
        sprintf(target_file_path, "%s/%s", path, file -> d_name);
        remove(target_file_path);
        if (DT_DIR == file -> d_type)
        {
            DeleteDirectory(target_file_path);
        }
    }
    closedir(dir);
    rmdir(path);
    return 0;
}
/* Copy a directory */
int CopyDirectory(const char *src_path, const char *dest_path)
{
    DIR *dir = NULL;
    struct dirent *file = NULL;
    dir = opendir(src_path);
    if (NULL == dir)
    {
        return -1;
    }
    CreateDirectory(dest_path);
    while (file = readdir(dir))
    {
        char src_file_path[NAME_MAX] = {};
        char dest_file_path[NAME_MAX] = {};
        if (!strcmp(".", file -> d_name) || !strcmp("..", file -> d_name))
        {
            continue;
        }
        sprintf(src_file_path, "%s/%s", src_path, file -> d_name);
        sprintf(dest_file_path, "%s/%s", dest_path, file -> d_name);
        CopyFile(src_file_path, dest_file_path);
        if (DT_DIR == file -> d_type)
        {
            CopyDirectory(src_file_path, dest_file_path);
        }
    }
    closedir(dir);
    return 0;
}
/* List a Directory's file */
int ListDirectoryFile(const char *root_path, void (*handle)(const char *root_path, const char *file_name, int type, void *ext_argument), void *ext_argument)
{
    DIR *dir = NULL;
    struct dirent *file = NULL;
    dir = opendir(root_path);
    if (NULL == dir)
    {
        return -1;
    }
    while (file = readdir(dir))
    {
        if (!strcmp(".", file -> d_name) || !strcmp("..", file -> d_name))
        {
            continue;
        }
        handle(root_path, file -> d_name, file -> d_type, ext_argument);
    }
    closedir(dir);
    return 0;
}
/* Traverse a Directory */
int TraverseDirectory(const char *root_path, void (*handle)(const char *root_path, const char *file_name, int type, void *ext_argument), void *ext_argument)
{
    DIR *dir = NULL;
    struct dirent *file = NULL;
    dir = opendir(root_path);
    if (NULL == dir)
    {
        return -1;
    }
    while (file = readdir(dir))
    {
        if (!strcmp(".", file -> d_name) || !strcmp("..", file -> d_name))
        {
            continue;
        }
        handle(root_path, file -> d_name, file -> d_type, ext_argument);
        if (DT_DIR == file -> d_type)
        {
            char sub_directory_path[NAME_MAX] = {};
            sprintf(sub_directory_path, "%s/%s", root_path, file -> d_name);
            TraverseDirectory(sub_directory_path, handle, ext_argument);
        }
    }
    closedir(dir);
    return 0;
}
/* 
    WriteFile
        Write new content to a file
    Argument
        file_name
            The file want to write
        content
            The content want to store
        size
            The size of content
    Return Value
        0
            Success
        -1
            Failed
*/
int WriteFile(const char *file_name, char *content, unsigned size)
{
    int fd = 0;
    fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (fd < 0)
    {
        return -1;
    }
    write(fd, content, size);
    close(fd);
    return 0;
}
/* 
    MoveFile
        Move a file
    Argument
        src_path
            The src file path
        dest_path
            The dest file path
    Return Value
        0
            Success
        -1
            Failed
*/
int MoveFile(const char *src_path, const char *dest_path)
{
    return rename(src_path, dest_path);
}
/* 
    CopyFile
        Copy a file
    Argument
        src_path
            The src file path
        dest_path
            The dest file path
    Return Value
        0
            Success
        -1
            Failed
*/
int CopyFile(const char *src_path, const char *dest_path)
{
    char *content = NULL;
    content = ReadFileContent(src_path);
    if (NULL == content)
    {
        return -1;
    }
    WriteFile(dest_path, content, strlen(content));
    free(content);
    return 0;
}
/* 
    ReadFileContent
        Load the file full content to the buffer
    Arguemnt
        file_name
            The file want to read
    Return Value
        The buffer to the file content
        If failed, return NULL
*/
char* ReadFileContent(const char *file_name)
{
    char *result = NULL;
    unsigned size = 0;
    int file_handle = 0;
    file_handle = open(file_name, O_RDONLY);
    if (file_handle < 0)
    {
        return NULL;
    }
    size = lseek(file_handle, 0, SEEK_END);
    lseek(file_handle, 0, SEEK_SET);
    result = calloc(size + 1, sizeof(char));
    if (NULL == result)
    {
        close(file_handle);
        return NULL;
    }
    read(file_handle, result, size);
    close(file_handle);
    return result;
}